package wxpay

import (
	"bgs/db"
	sqlc "bgs/db/sqlc"
	userService "bgs/service/user"
	"bgs/util"
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"github.com/nsqio/go-nsq"
	"go.uber.org/zap"
	"strconv"
)

var (
	lastBatchTransferNotification *BatchTransferNotification = nil
)

// 转账通知
type BatchTransferNotification struct {
	// 转账成功的订单
	SuccessOrders []*transferOrder `json:"success_orders"`
	// 转账失败的订单
	FailedOrders []*transferOrder `json:"failed_orders"`
	Timestamp    int64            `json:"timestamp"`
}

// 转账订单详情
type transferOrder struct {
	// 订单号
	OutDetailNo string `json:"out_detail_no"`
	// 业务订单号
	BizDetailNo string `json:"biz_detail_no"`
	// 金额
	Amount int64 `json:"amount"`
	// 备注
	Remark string `json:"remark"`
	// 用户openID
	OpenID string `json:"open_id"`
	// 状态 "SUCCESS" "FAIL"
	Status string `json:"status"`
	// 失败原因
	FailReason string `json:"fail_reason"`
}

type UserBatchTransferHandler struct {
	dao         *db.PgDao
	slog        *zap.SugaredLogger
	userService *userService.UserServiceImp
}

// NewUserBatchTransferHandler create a new handler for user transfer
func NewUserBatchTransferHandler(
	pgdao *db.PgDao,
	slog *zap.SugaredLogger,
	userService *userService.UserServiceImp,
) *UserBatchTransferHandler {
	return &UserBatchTransferHandler{
		dao:         pgdao,
		slog:        slog.With("nsq_consumer_handler", "UserBatchTransferHandler"),
		userService: userService,
	}
}

type UserWithdrawMap map[int64]int64
type UserAccountPtrMap map[int64]*sqlc.UserAccount

// HandleMessage implements the Handler interface.
func (h *UserBatchTransferHandler) HandleMessage(m *nsq.Message) error {
	if len(m.Body) == 0 {
		// Returning nil will automatically send a FIN command to NSQ to mark the message as processed.
		return nil
	}
	h.slog.Infof("message=>%s", string(m.Body))

	v := &BatchTransferNotification{}
	if decodeErr := json.NewDecoder(bytes.NewReader(m.Body)).Decode(v); decodeErr != nil {
		return fmt.Errorf("无法解析消息[BatchTransferNotification]: %s", decodeErr.Error())
	}

	if lastBatchTransferNotification != nil && lastBatchTransferNotification.Timestamp >= v.Timestamp {
		h.slog.Infof("过时的消息: lastBatchTransferNotification.Timestamp:%v,v.Timestamp:%v", lastBatchTransferNotification.Timestamp, v.Timestamp)
		return nil
	}
	reqID := strconv.FormatInt(v.Timestamp, 10)
	slog := h.slog.With("reqID", reqID)
	// 将来抽象到对应的业务组件
	ctx := context.WithValue(context.Background(), util.RequestID{}, reqID)

	err := h.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {

		outDetailNosOfSuccess := util.MapToString(util.ToGenericSlice(v.SuccessOrders), func(v interface{}) string {
			val := v.(*transferOrder)
			return val.OutDetailNo
		})
		outDetailNosOfFail := util.MapToString(util.ToGenericSlice(v.FailedOrders), func(v interface{}) string {
			val := v.(*transferOrder)
			return val.OutDetailNo
		})

		userAccountWithdrawApplymentsOfSuccess, err := q.ListUserAccountWithdrawApplymentsByOutDetailNos(ctx, outDetailNosOfSuccess)
		if err != nil {
			return err
		}
		slog.Infof("a1 批量获取成功的用户提现申请 ok")

		userAccountWithdrawApplymentsOfFail, err := q.ListUserAccountWithdrawApplymentsByOutDetailNos(ctx, outDetailNosOfFail)
		if err != nil {
			return err
		}
		slog.Infof("a2 批量获取失败的用户提现申请 ok")

		var idsOfSuccess []int64
		var idsOfFail []int64

		var userIDsToLock []int64
		userWithdrawMapOfSuccess := make(UserWithdrawMap)
		userWithdrawMapOfFail := make(UserWithdrawMap)

		util.Each(util.ToGenericSlice(userAccountWithdrawApplymentsOfSuccess), func(v interface{}) {
			val := v.(sqlc.UserAccountWithdrawApplyment)
			k := val.UserID

			idsOfSuccess = append(idsOfSuccess, val.ID)
			userIDsToLock = append(userIDsToLock, k)
			if withdrawAmount, ok := userWithdrawMapOfSuccess[k]; ok {
				userWithdrawMapOfSuccess[k] = withdrawAmount + val.Amount
			} else {
				userWithdrawMapOfSuccess[k] = val.Amount
			}
		})
		util.Each(util.ToGenericSlice(userAccountWithdrawApplymentsOfFail), func(v interface{}) {
			val := v.(sqlc.UserAccountWithdrawApplyment)
			k := val.UserID

			idsOfFail = append(idsOfFail, val.ID)
			userIDsToLock = append(userIDsToLock, k)
			if withdrawAmount, ok := userWithdrawMapOfFail[k]; ok {
				userWithdrawMapOfFail[k] = withdrawAmount + val.Amount
			} else {
				userWithdrawMapOfFail[k] = val.Amount
			}
		})

		// 仅仅是冻结,不使用
		userAccountPtrMap := make(UserAccountPtrMap)
		for _, userID := range userIDsToLock {
			userAccount, err2 := h.userService.EnsureAccountInfo(ctx, q, true, userID)
			if err2 != nil {
				err = err2
				return
			}
			userAccountPtrMap[userID] = &userAccount
		}
		slog.Infof("b 冻结提现申请的用户总账记录 ok")
	
		if len(idsOfSuccess) > 0 {
			err = h.userService.BatchUpdateAccountWithdrawApplymentApplyState(ctx, q, userService.BatchUpdateAccountWithdrawApplymentApplyStateParam{
				IDs:        idsOfSuccess,
				ApplyState: "SUCCESS",
			})
			if err != nil {
				return err
			}
			slog.Infof("c1 批量更新成功的用户提现申请 ok")

			err = h.userService.BatchLoggingAccountForUserTransfer(ctx, q, userAccountWithdrawApplymentsOfSuccess)
			if err != nil {
				return err
			}
			slog.Infof("d1 成功的用户提现申请批量记明细账 ok")


			 
			for userID, withdrawAmount := range userWithdrawMapOfSuccess {
				userAccountPtr := userAccountPtrMap[userID]
				h.userService.UpdateAccountForUserTransferSuccessWithLock(ctx, q, userService.UpdateAccountForUserTransferSuccessWithLockParam{
					UserAccountPtr: userAccountPtr,
					WithdrawAmount: withdrawAmount,
				})
			}

			slog.Infof("e1 成功用户提现申请批量更新用户总账(提现金额中的进入已提现) ok")
		}

		if len(idsOfFail) > 0 {
			err = h.userService.BatchUpdateAccountWithdrawApplymentApplyState(ctx, q, userService.BatchUpdateAccountWithdrawApplymentApplyStateParam{
				IDs:        idsOfFail,
				ApplyState: "FAIL",
			})
			if err != nil {
				return err
			}
			slog.Infof("c2 批量更新失败的用户提现申请 ok")

			for userID, withdrawAmount := range userWithdrawMapOfFail {
				userAccountPtr := userAccountPtrMap[userID]
				h.userService.UpdateAccountForUserTransferFailWithLock(ctx, q, userService.UpdateAccountForUserTransferFailWithLockParam{
					UserAccountPtr: userAccountPtr,
					WithdrawAmount: withdrawAmount,
				})
			}
			slog.Infof("d2 失败用户提现申请批量更新用户总账(提现金额中的退回到可用余额) ok")
		}

		return
	})
	if err != nil {
		return err
	}
	slog.Infof("消息[BatchTransferNotification]处理成功")

	lastBatchTransferNotification = v
	// Returning a non-nil error will automatically send a REQ command to NSQ to re-queue the message.
	return nil
}
